/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.jeff.springmvcstudy.controller;

import com.jeff.springmvcstudy.ui.HelloJSON;
import com.jeff.springmvcstudy.ui.ReqCountEntity;
import com.jeff.springmvcstudy.util.LogUtil;
import com.jeff.springmvcstudy.util.ViewManager;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;


/**
 * The first controller, for tutorial only.
 * 
 * Note: it is welcome to hide Controller impl class behind an interface(in such
 *  case we can apply DynamicProxy AOP on the controller behavior), but if that 
 *  is the case, all the following mapping annotations must put on inteface
 *  instead of this impl class.
 * @author jeff
 */
@Controller
@RequestMapping("/hello")
@SessionAttributes("reqCount")
public class HelloController {
    private static final Logger LOG = LogUtil.getLogger(HelloController.class);
    
    /*
     * 1. ModelAndView encapsulates the behavior info(including viewName, modelName, and modelObject.
     *      Is it the concept of "context" in DCI?
     */
    @RequestMapping("/sayHello")
    public ModelAndView sayHello() {
        String message = "Hello Spring MVC 3";
        LOG.log(Level.INFO, message);

        String viewName = ViewManager.VIEW_HELLO;
        String modelName = "message";
        Object modelObject = message;
        
        return new ModelAndView(viewName, modelName, modelObject);
    }
    
    /*
     * Features presented:
     * 1. The URI template is almost the same as jersey RESTful webservice impl
     * 2. @PathVariable used almost same as @PathParam in jersey
     */
    @RequestMapping("/calculate/add/{num1}/{num2}")
    public ModelAndView addNums(@PathVariable("num1") double num13, @PathVariable double num2) {
        Double result = num13 + num2;
        LOG.log(Level.INFO, result.toString());
        
        return new ModelAndView(ViewManager.VIEW_CALC, "calcResult", result);
    }
    
    /*
     * Fetures presented:
     * 1. accept request parameter by @RequestParam
     * 2. same as @PathVariable, @RequestParam will by default believe 
     *  the method arg name literately same as the http arg name. User can also 
     *  specify the literate arg name if it differs from method arg name.
     * 3. specify Http Method
     * 4. specify producible type(can also specify consumable type, say 
     *  when request body is in JSON by jQuery)
     * 5. @ResponseBody tells the controller the return value is not a view name 
     *  but a data object as the http response body.
     */
    @RequestMapping(value="/json/{target}", method = RequestMethod.GET, produces="application/json")
    public @ResponseBody HelloJSON getHelloJson(
            @PathVariable String target, 
            @RequestParam("arg1") String argX, 
            @RequestParam int arg2) {
        HelloJSON hello = new HelloJSON(target, new Date(), argX + arg2);
        LOG.log(Level.INFO, target);
        
        return hello;
    }
    
    /*
     * Features presented:
     * 1. This demo ranges across displayHelloJSON() until overrideDummyModelAttribute4().
     * 2. @ModelAttribute methods works like a simple interceptor BEFORE any @RequestMapping method
     * 3. If multiple @ModelAttribute methods defined, try to avoid they handle
     *  the same attribute(object), the final behavior will be difficult to 
     *  identify BECAUSE their exec ordering are not guaranteed.
     */
    @RequestMapping("/testModelAttribute")
    public String displayHelloJSON() {
        return ViewManager.VIEW_HELLO_MODEL;
    }
    @ModelAttribute("hello")
    public HelloJSON addDummyModelAttribute() {
        return new HelloJSON("Jeff", new Date(), "A Dummy HelloJSON");
    }
    @ModelAttribute("hello")
    public HelloJSON addDummyModelAttribute1() {
        return new HelloJSON("Jeff1", new Date(), "A Dummy HelloJSON");
    }
    @ModelAttribute
    public void overrideDummyModelAttribute3(Model model) {
        model.addAttribute("hello", new HelloJSON("Jeff4", new Date(), "Another Dummy HelloJSON"));
    }
    @ModelAttribute
    public void overrideDummyModelAttribute4(Model model) {
        model.addAttribute("hello", new HelloJSON("Jeff3", new Date(), "Another Dummy HelloJSON"));
    }
    
    
    /*
     * Note here the attribute("reqCount") will be initiated by addReqCount2SessionAttribute()
     * and then kept in session(see the type-level @SessionAttribute on this controller class)
     * until sessionStatus.setComplete() is called.
     */
    @RequestMapping("/requestCount/{switchFlag}")
    @ResponseBody
    public int getConversationCount(
            @PathVariable int switchFlag,
            @ModelAttribute("reqCount") ReqCountEntity reqCount,
            SessionStatus sessionStatus) {
        if (switchFlag == 0) {
            sessionStatus.setComplete();
            return 0;
        }
        
        reqCount.setReqCount(reqCount.getReqCount() + 1);
        LOG.log(Level.INFO, "reqCount now is: " + reqCount.getReqCount());
        return reqCount.getReqCount();
    }
    @ModelAttribute("reqCount") //@ModelAttribute method for a certain attribute will be run only once
    public ReqCountEntity addReqCount2SessionAttribute() {
        LOG.info("ReqCount is created");
        return new ReqCountEntity();
    }
    
    
    @RequestMapping("/showHeader")
    @ResponseBody
    public String showHeader(
            @RequestHeader("Accept-Encoding") String encoding) {
        LOG.info("Encoding = " + encoding);
        return encoding;
    }
    
    @RequestMapping("/intercepted/{interceptorId}")
    @ResponseBody
    public String gotIntercepted(@PathVariable int interceptorId) {
        LOG.info("Inside the gotIntercepted() method");
        return "gotIntercepted(" + interceptorId + ") called";
    }
    
}
